﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Lucene.Linq.Mapping;

namespace Lucene.Linq.Expressions
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Query<T> : IOrderedQueryable<T>
    {
        internal QueryProvider Provider { get; set; }
        internal Expression Expression { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public string QueryString
        {
            get { return Provider.QueryString.ToString(); }
        }

        /// <summary>
        /// 
        /// </summary>
        public string Name
        {
            get { return Details.Document.Name; }
        }

        internal IndexContext Context { get; private set; }

        internal IndexDetails Details { get; private set; }

        #region Constructors

        internal Query(IndexContext context, IndexDetails details)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (details == null) throw new ArgumentNullException("details");

            Context = context;
            Details = details;
            Provider = new QueryProvider(context, details);
            Expression = Expression.Constant(this);
        }


        internal Query(IndexContext context, IndexDetails details, Expression expression)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            Context = context;
            Details = details;
            Provider = new QueryProvider(context, details);

            if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type))
            {
                throw new ArgumentOutOfRangeException("expression");
            }

            Expression = expression;
        }

        /*

        internal Query(QueryProvider provider) {
            if (provider == null) {
                throw new ArgumentNullException("provider");
            }

            this.Provider = provider;
            this.Expression = Expression.Constant(this);
        }

        internal Query(QueryProvider provider, Expression expression) {
            if (provider == null) {
                throw new ArgumentNullException("provider");
            }

            if (expression == null) {
                throw new ArgumentNullException("expression");
            }

            if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type)) {
                throw new ArgumentOutOfRangeException("expression");
            }

            this.Provider = provider;
            this.Expression = expression;
        }
         * */

        #endregion

        #region IOrderedQueryable<T> Members

        Expression IQueryable.Expression
        {
            get { return Expression; }
        }

        Type IQueryable.ElementType
        {
            get { return typeof(T); }
        }

        IQueryProvider IQueryable.Provider
        {
            get { return Provider; }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return ((IEnumerable<T>)Provider.Execute(Expression)).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)Provider.Execute(Expression)).GetEnumerator();
        }

        #endregion

        public override string ToString()
        {
            return Provider.QueryString.ToString();
        }
    }
}